# 5. Control Raspberry Pi Camera by APP ## Control Raspberry Pi Camera by APP Note:The video streaming sent to App is processed using opencv, so the opencv needs installing on Raspberry Pi. We recommend using the Raspberry Pi imager system which has opencv and tensorflow. And the Raspberry Pi hotspot will be actived when booting up. Note: need to turn off the programme of boot-up, then follow the below instructions Open the terminal and input **sudo nano /etc/rc.local** then press Enter ![](/media/3db288837d9f1fa26f3fefed5db2eaad.png) Add # before enabling the scripts, as shown below; ![](/media/fd09271425f350ce513d497bd8b0c079.png) Press **Ctrl + o** to save, then press Enter and **Ctrl + X** to exit editing There are two ways of communication between smartphone APP and RaspberryPi: Bluetooth communication and WiFi network communication. In order to achieve video streaming and camera monitoring, WiFi network communication is used because Bluetooth communication can not work out this function. The TCP protocol sends commands to control the movement of the car and the UDP protocol receives the video streaming sent by the Raspberry Pi. The module library for TCP and UDP communication in Python is generally“socket”which provides the standard BSD Sockets API. To learn more about“socket”, please follow this link: [https://docs.python.org/3/howto/sockets.html](https://docs.python.org/3/howto/sockets.html) ## 1. APP communicates with Raspberry Pi through TCP When the smartphone is connected to Raspberry Pi hotspot or smartphone and Raspberry Pi in the same LAN (connect to the same WiFi), TCP protocol is used for communication. Raspberry Pi is server side, the smartphone APP is the client side. **1.1. APP download and installation:** (1) We provide “ keyes RPi Robot.apk” app. ![Img](./media/img-20250304083543.png) (2) You can also download it from “Google play”: Search **keyes RPi Robot** in the ![](/media/f79081e49dce555af5787d44d6cf5411.png) **1.2. APP interface:** ![](/media/5344b0c8e1083caab067f17a9d786822.png) ![](/media/6ce694501ab0aaa2412ec1b472b8810a.png) **1.3. Code:** ```python import socket import time from OledModule.OLED import OLED import RPi.GPIO as GPIO GPIO.setwarnings(False) def getLocalIp(): '''Get the local ip''' try: s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket s.connect(('8.8.8.8',80)) ip=s.getsockname()[0] # The returned IP address is obtained finally: s.close() return ip def cameraAction(command): if command=='CamUp': print("camUp") elif command=='CamDown': print("camDown") elif command=='CamLeft': print("camLeft") elif command=='CamRight': print("camRight") elif command=='CamStop': print("camStop") def motorAction(command): '''Set the action of motor according to the command''' if command=='DirForward': print("go") elif command=='DirBack': print("back") elif command=='DirLeft': print("left") elif command=='DirRight': print("right") elif command=='DirStop': print("stop") def main(): host=getLocalIp() print('localhost ip :'+host) port=5051 #Init the tcp socket tcpServer=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # Create a TCP socket tcpServer.bind((host,port)) tcpServer.setblocking(0) #Set unblock mode tcpServer.listen(5) # Start listening for TCP incoming connections #Init oled module oled=OLED() oled.setup() oled.writeArea1(host) oled.writeArea3('State:') oled.writeArea4(' Disconnect') while True: try: time.sleep(0.001) # Accepts a TCP connection and returns (connect,address), # where connect is the new socket object that can be used to receive and send data. # Address is the address of the connected client. (client,addr)=tcpServer.accept() print('accept the client!') oled.writeArea4(' Connect') client.setblocking(0) # Set the socket to non-blocking mode while True: time.sleep(0.001) try: #Accepts TCP socket data. The data is returned as a string data=client.recv(1024) data=bytes.decode(data) # Get the final data if(len(data)==0): print('client is closed') oled.writeArea4(' Disconnect') break motorAction(data) # Passes the received data to the function cameraAction(data) except socket.error: continue except KeyboardInterrupt as e: raise e except socket.error: pass except KeyboardInterrupt: clear() tcpServer.close() oled.clear() port = 0000 except Exception as e1: clear() tcpServer.close() oled.clear() main() # Enter main program ``` **1.4. Code explanation:** This code is just for the application of socket Socket information:[https://docs.python.org/3/howto/sockets.html](https://docs.python.org/3/howto/sockets.html) port=5051 As the port number set by the server, Raspberry PI can be modified by yourself. The port number of APP should be the same as this one **1.5. Run code & test result:** Enter the following two commands in the terminal respectively and press “Enter” key respectively: `cd /home/pi/RaspberryPi-Car` `sudo python3 socket\_test.py` After running the code, the terminal will display Raspberry Pi IP address. ![](/media/eefdd7ce065d02519eaa45566d416fcf.png) The OLED screen also shows the IP address of the Raspberry Pi and the connection status of the APP ( Disconnect). Connect the phone to the hotspot of the Raspberry Pi or make them connect to the same same WiFi, then open the APP to enter the IP address and port number,. For example, the address of my Raspberry Pi is **192.168.1.126** and the port number is **5051**. ![](/media/666de8b0c9fed2e9e469b3c536c41e94.png) Click “**ENTER**” to connect. If the connection is successful, the terminal will display“accept the client\!”. ![](/media/56b8b1439884a5db667b92a988686cc2.png) The connection status of the OLED display will change into “**Connect**”. Then click the button on the APP interface to send data. ![](/media/649625aba3387f9b94598cbe10f467d9.png) Note: press“Ctrl +C”to stop executing commands. If you encounter the following problem, this means that the background running failed to close. So you need to close the APP for a while or modify the port number. ![](/media/50b1386c6e34e420c8ce6b647dff5ac5.png) Command “**jobs**” can check the running process in the background. ## 2. Control the car via APP In the previous lesson, we learned how to communicate with App and the Raspberry Pi. Then we will acquire how to control the car via app. **2.1. Code:** ```python import socket import time from OledModule.OLED import OLED import RPi.GPIO as GPIO GPIO.setwarnings(False) servoPin1 = 5 servoPin2 = 6 servoPin3 = 7 angle1 = 90 angle2 = 90 GPIO.setup(servoPin1, GPIO.OUT) GPIO.setup(servoPin2, GPIO.OUT) GPIO.setup(servoPin3, GPIO.OUT) def servoPulse(servoPin, myangle): pulsewidth = (myangle*11) + 500 GPIO.output(servoPin,GPIO.HIGH) time.sleep(pulsewidth/1000000.0) GPIO.output(servoPin,GPIO.LOW) time.sleep(20.0/1000 - pulsewidth/1000000.0) L_IN1 = 20 L_IN2 = 21 L_PWM1 = 0 L_IN3 = 22 L_IN4 = 23 L_PWM2 = 1 R_IN1 = 24 R_IN2 = 25 R_PWM1 = 12 R_IN3 = 26 R_IN4 = 27 R_PWM2 = 13 GPIO.setmode(GPIO.BCM) # use BCM numbers #set the MOTOR Driver Pin OUTPUT mode GPIO.setup(L_IN1,GPIO.OUT) GPIO.setup(L_IN2,GPIO.OUT) GPIO.setup(L_PWM1,GPIO.OUT) GPIO.setup(L_IN3,GPIO.OUT) GPIO.setup(L_IN4,GPIO.OUT) GPIO.setup(L_PWM2,GPIO.OUT) GPIO.setup(R_IN1,GPIO.OUT) GPIO.setup(R_IN2,GPIO.OUT) GPIO.setup(R_PWM1,GPIO.OUT) GPIO.setup(R_IN3,GPIO.OUT) GPIO.setup(R_IN4,GPIO.OUT) GPIO.setup(R_PWM2,GPIO.OUT) GPIO.output(L_IN1,GPIO.LOW) GPIO.output(L_IN2,GPIO.LOW) GPIO.output(L_IN3,GPIO.LOW) GPIO.output(L_IN4,GPIO.LOW) GPIO.output(R_IN1,GPIO.LOW) GPIO.output(R_IN2,GPIO.LOW) GPIO.output(R_IN3,GPIO.LOW) GPIO.output(R_IN4,GPIO.LOW) #set pwm frequence to 1000hz pwm_R1 = GPIO.PWM(R_PWM1,100) pwm_R2 = GPIO.PWM(R_PWM2,100) pwm_L1 = GPIO.PWM(L_PWM1,100) pwm_L2 = GPIO.PWM(L_PWM2,100) #set inital duty cycle to 0 pwm_R1.start(0) pwm_L1.start(0) pwm_R2.start(0) pwm_L2.start(0) def ahead(): GPIO.output(L_IN1,GPIO.LOW) #Upper Left forward GPIO.output(L_IN2,GPIO.HIGH) pwm_L1.ChangeDutyCycle(80) GPIO.output(L_IN3,GPIO.HIGH) #Lower left forward GPIO.output(L_IN4,GPIO.LOW) pwm_L2.ChangeDutyCycle(80) GPIO.output(R_IN1,GPIO.HIGH) #Upper Right forward GPIO.output(R_IN2,GPIO.LOW) pwm_R1.ChangeDutyCycle(80) GPIO.output(R_IN3,GPIO.LOW) #Lower Right forward GPIO.output(R_IN4,GPIO.HIGH) pwm_R2.ChangeDutyCycle(80) def left(): GPIO.output(L_IN1,GPIO.HIGH) GPIO.output(L_IN2,GPIO.LOW) pwm_L1.ChangeDutyCycle(80) GPIO.output(L_IN3,GPIO.LOW) GPIO.output(L_IN4,GPIO.HIGH) pwm_L2.ChangeDutyCycle(80) GPIO.output(R_IN1,GPIO.HIGH) GPIO.output(R_IN2,GPIO.LOW) pwm_R1.ChangeDutyCycle(80) GPIO.output(R_IN3,GPIO.LOW) GPIO.output(R_IN4,GPIO.HIGH) pwm_R2.ChangeDutyCycle(80) def right(): GPIO.output(L_IN1,GPIO.LOW) GPIO.output(L_IN2,GPIO.HIGH) pwm_L1.ChangeDutyCycle(80) GPIO.output(L_IN3,GPIO.HIGH) GPIO.output(L_IN4,GPIO.LOW) pwm_L2.ChangeDutyCycle(80) GPIO.output(R_IN1,GPIO.LOW) GPIO.output(R_IN2,GPIO.HIGH) pwm_R1.ChangeDutyCycle(80) GPIO.output(R_IN3,GPIO.HIGH) GPIO.output(R_IN4,GPIO.LOW) pwm_R2.ChangeDutyCycle(80) def rear(): GPIO.output(L_IN1,GPIO.HIGH) GPIO.output(L_IN2,GPIO.LOW) pwm_L1.ChangeDutyCycle(80) GPIO.output(L_IN3,GPIO.LOW) GPIO.output(L_IN4,GPIO.HIGH) pwm_L2.ChangeDutyCycle(80) GPIO.output(R_IN1,GPIO.LOW) GPIO.output(R_IN2,GPIO.HIGH) pwm_R1.ChangeDutyCycle(80) GPIO.output(R_IN3,GPIO.HIGH) GPIO.output(R_IN4,GPIO.LOW) pwm_R2.ChangeDutyCycle(80) def stop(): pwm_L1.ChangeDutyCycle(0) pwm_L2.ChangeDutyCycle(0) pwm_R1.ChangeDutyCycle(0) pwm_R2.ChangeDutyCycle(0) def clear(): GPIO.cleanup() def getLocalIp(): '''Get the local ip''' try: s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) s.connect(('8.8.8.8',80)) ip=s.getsockname()[0] finally: s.close() return ip def cameraAction(command): global angle1 global angle2 if command=='CamUp': angle1 = angle1 - 1 servoPulse(servoPin2, angle1) if(angle1 <= 0): angle1 = 0 elif command=='CamDown': angle1 = angle1 + 1 servoPulse(servoPin2, angle1) if(angle1 >= 180): angle1 = 180 elif command=='CamLeft': angle2 = angle2 + 1 servoPulse(servoPin3, angle2) if(angle2 >= 180): angle2 = 180 elif command=='CamRight': angle2 = angle2 - 1 servoPulse(servoPin3, angle2) if(angle2 <= 0): angle2 = 0 def motorAction(command): '''Set the action of motor according to the command''' if command=='DirForward': print("go") ahead() elif command=='DirBack': print("back") rear() elif command=='DirLeft': print("left") left() elif command=='DirRight': print("right") right() elif command=='DirStop': print("stop") stop() def setCameraAction(command): if command=='CamUp' or command=='CamDown' or command=='CamLeft' or command=='CamRight': return command else: return 'CamStop' def main(): ks = 'keyestudio' #Init oled module oled=OLED() oled.setup() oled.writeArea1(ks) oled.writeArea3('State:') oled.writeArea4(' Disconnect') time.sleep(2) #Must add delay '''The main thread, control the motor''' host=getLocalIp() print('localhost ip :'+host) port=5051 #Init the tcp socket tcpServer=socket.socket(socket.AF_INET,socket.SOCK_STREAM) tcpServer.bind((host,port)) tcpServer.setblocking(0) #Set unblock mode tcpServer.listen(5) global cameraActionState #Set a state variable for steering module cameraActionState='CamStop' #Init oled module oled=OLED() oled.setup() oled.writeArea1(host) oled.writeArea3('State:') oled.writeArea4(' Disconnect') while True: try: time.sleep(0.001) (client,addr)=tcpServer.accept() print('accept the client!') oled.writeArea4(' Connect') client.setblocking(0) while True: time.sleep(0.001) cameraAction(cameraActionState) try: data=client.recv(1024) data=bytes.decode(data) if(len(data)==0): print('client is closed') oled.writeArea4(' Disconnect') break motorAction(data) cameraActionState=setCameraAction(data) except socket.error: continue except KeyboardInterrupt as e: raise e except socket.error: pass except KeyboardInterrupt: tcpServer.close() oled.clear() print("close") port = 0000 except Exception as e1: tcpServer.close() oled.clear() main() ``` **2.2. Run code and test result:** Enter the following two commands in the terminal respectively and press “Enter” key respectively: `cd /home/pi/RaspberryPi-Car` `sudo python3 MainControl.py` After running the code, connect your phone connects to the hotspot of Raspberry Pi. Then open the APP, enter the IP address of the Raspberry Pi and the port number. Next, press the APP interface buttons to control the car and the pan tilt of camera. ## 3. UDP protocol for camera monitoring Raspberry Pi transmits the video streaming to the APP through UDP protocol and the App displays the monitoring screen. The principle is to send each frame of the video captured by the camera to the APP, frame by frame, and the APP interface continuously displays each frame of the picture, which becomes a video. When we use “**openvc**” to process the video frames, use the command “**capture=cv2.VideoCapture(0)**” to activate the camera, draw on the command **success,frame=capture.read()** to get videos by frame, send the command **cv2.imencode** to compress picture and transmit data then input the command **server.sendall** to send out. **3.1. Code:** ```python import cv2 import numpy import socket import time import struct HOST='10.0.0.222' #HOST='192.168.1.222' PORT=5051 # 1024-5000 WIDTH=320 HEIGHT=240 server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # create a UDP server.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1) #enable broadcast # Can be used to specify the destination address/port server.connect((HOST,PORT)) print('now starting to send frames...') capture=cv2.VideoCapture(0) # Enabling the Camera # Sets the width and height of the image capture.set(cv2.CAP_PROP_FRAME_WIDTH,WIDTH) capture.set(cv2.CAP_PROP_FRAME_HEIGHT,HEIGHT) try: while True: time.sleep(0.01) success,frame=capture.read() # Read the video by frame if success and frame is not None: result,imgencode=cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,95]) #result,imgencode=cv2.imencode('.webp',frame,[cv2.IMWRITE_WEBP_QUALITY,20]) print(len(imgencode)) server.sendall(imgencode) #print('have sent one frame') except Exception as e: server.sendall(struct.pack('b',1)) print(e) capture.release() server.close() ``` **3.2. Code explanation:** **HOST='10.0.0.222'** Modify into the IP address when the app connects to the hotspot of the Raspberry Pi. Open WiFi attributes to check. **server.connect((HOST,PORT))** Generally, the“sendto”function will be used to send data directly after the“UDP socket”is established, and the destination address/port must be specified in the parameters of the“sendto”function. But after the“UDP socket”is established, the“connect”function is used to specify the destination address/port, and the“send”and“sendall” functions can also be used to send data, because they already know each other’s address and port, and this information can also be obtained with“getsockname”. **success,frame=capture.read()** You can get videos by frame via **capture.read()**,success and frame are return values to obtain **capture.read()**. Capture is boolean value; if the frame is correct, return True; otherwise, False. Frame is the image of each frame and also a three-dimensional matrix. **result,imgencode=cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,95])** **cv2.imencode():** transfer the picture format into the flow data and assign values to the RAM; used to compression of the picture data format. **'.jpg':** encode the picture frame with jpg format. **cv2.CV_IMWRITE_JPEG_QUALITY** :set picture format to .jpeg or .jpg picture quality,value is 0---100(the larger the value, the higher the picture quality),default as 95. **cv2.sendall()** **sendall():** It will automatically determine the amount of content to be sent each time, then remove the part already sent from the total content and pass the rest on to “ **send()** ” for sending. This means that all the data will be sent out. **3.3. Run code and test result:** Enter the following two commands in the terminal respectively and press “Enter” key respectively: `cd /home/pi/RaspberryPi-Car` `sudo python3 FramesSend_test.py` After running the code, the terminal prints out the data length of each image sent out. ![](/media/237e8edcbbd772e60d2bac7657c7b7c0.png) Connect the phone to the hotspot of Raspberry Pi first and open the APP. You only need to enter the correct port number (5051) in the code, and IP address is not needed. Click “ENTER” to check the real-time camera monitoring. ## 4. Automatically obtain the IP address of your phone After connecting the phone to the Raspberry PI hotspot in the last class, I had to check the IP address assigned to the phone, and then fill it in the code, which was not very convenient to use. So we came up with a way for raspberry PI to automatically recognize the IP address of the phone. It is intricate to obtain the IP address by checking cellphones. Therefore, we can use the Raspberry Pi to recognize the IP address. So we need to use ping App to get the IP address of the Raspberry Pi Note: if you don’t have the Raspberry Pi imager we provide, you need to install scapy Enter the following two commands in the terminal respectively and press “Enter” key respectively: `sudo pip3 install scapy-python3` **4.1. Code:** ```python #!/usr/bin/python import cv2 import numpy import socket import time import struct from scapy.all import * IPData = [0,1] # Store the size of the IP address of the phone def Echo(packet): IP=(packet['IP'].src) Bigx=len(packet[Raw]) global IPData IPData[0]=IP IPData[1]=Bigx print ("source IP:",IPData[0]) print("Packet size:",IPData[1]) def Stop(packet): #if IPData[1] == 1111: return True #sniff(iface="wlan0",filter="icmp[icmptype] = icmp-echo",count=1,prn=Echo,stop_filter=Stop) #sniff(iface="eth0",filter="icmp[icmptype] = icmp-echo",count=1,prn=Echo,stop_filter=Stop) while(IPData[0] == 0)or(IPData[0] == '10.0.0.1'): print("wait..") sniff(iface="wlan0",filter="icmp[icmptype] = icmp-echo",count=0,prn=Echo,stop_filter=Stop) print("IP = ",IPData[0]) print ("source IP:",IPData[0]) HOST=IPData[0] # Assign the obtained IP address to HOST #HOST='10.0.0.222' #HOST='192.168.1.222' PORT=5051 WIDTH=320 HEIGHT=240 server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # create a UDP server.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1) #enable broadcast server.connect((HOST,PORT)) print('now starting to send frames...') capture=cv2.VideoCapture(0) capture.set(cv2.CAP_PROP_FRAME_WIDTH,WIDTH) capture.set(cv2.CAP_PROP_FRAME_HEIGHT,HEIGHT) try: while True: try: time.sleep(0.01) success,frame=capture.read() if success and frame is not None: result,imgencode=cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,95]) #result,imgencode=cv2.imencode('.webp',frame,[cv2.IMWRITE_WEBP_QUALITY,20]) #print(len(imgencode)) server.sendall(imgencode) #print('have sent one frame') except Exception as e: print(e) continue except Exception as e: server.sendall(struct.pack('b',1)) print(e) capture.release() server.close() ``` **4.2. Code explanation:** **sniff(iface="wlan0",filter="icmp[icmptype] = icmp-echo",count=1,prn=Echo,stop_filter=Stop)** **iface:** the NIC specified for packet capture. **Filter: ** filter rule. **Count:** the number of packets to capture, when set to 0 it is always captured. **Prn:** define a callback function for each packet. **stop_filter:** define a function. when it catches the specified data. **4.3. Run code and test result:** Enter the following two commands in the terminal respectively and press “Enter” key respectively: `cd /home/pi/RaspberryPi-Car` `sudo python3 FramesSend.py` After running code, connect the hotspot of the Raspberry Pi, open App, input the IP address of the Raspberry Pi and designated port number. You can open another terminal to check the IP address of the Raspberry Pi. The command is **ip a**. ![](/media/adb387adf0c3cbeec9932d8742b9cc97.png) 192.168.1.180 is the fixed IP address. Both of two addresses can be used. Click“ENTER”to see the IP address of the mobile phone displayed on the terminal. ![](/media/a245136a2f7125d4998c62b645ea192d.png) Then you can see the camera monitoring on the App. ## 5. Boot up the monitoring function of Raspberry Pi `sudo nano /etc/rc.local` Previously we have to enter the command to run the code in the terminal; nevertheless it won’t work out when rebooting the Raspberry Pi. In many cases, we want to be able to run a specific function when booting up, so we need to boot up the corresponding program. Just write the command to be activated in “**rc.local**” . Use the “**nano**” editor to open “**rc.local**” and write the start-up scripts for controlling the car and video surveillance. Enter the following two commands in the terminal respectively and press “Enter” key respectively: `sudo nano /etc/rc.local` ![](/media/e9e9a910ee06d0ebccd531e6370fbc4c.png) After writing the script, press "**Ctrl + O, Enter**" to save, and "**Ctrl +X**" to exit editing. Restart the Raspberry Pi system: **sudo reboot** After rebooting, connect the cellphone to the hotspot of Raspberry Pi. Then open the APP interface, entering the IP address and port number (5051) and click“ENTER”, then the connection is successful. The APP interface will show the camera monitoring, pressing buttons to control the car movement and camera action. If you fail, you can power off the Raspberry Pi and reboot it again.